#include "lsv_rcache.h"
#include "lsv_log.h"
#include "lsv_volume.h"
#include "list.h"

/*init read cache*/
int lsv_rcache_read_data(lsv_volume_proto_t *volume_proto, lsv_s8_t * buf, lsv_u32_t chunk_id, lsv_s32_t chunk_off, lsv_s32_t size) {
        int err = 0;

        err = lsv_volume_chunk_read_data(volume_proto,
                        LSV_THIS_VOL_INO,
                        chunk_id,
                        chunk_off,
                        size,
                        buf);
        if(unlikely(err)){
                DERROR("chunk[%d] load failed, err: %d\n", chunk_id, err);
                err = -1;
                return err;
        }

        return err;
}

int lsv_rcache_write_data(lsv_volume_proto_t *volume_proto, lsv_s8_t * buf, lsv_u32_t chunk_id,
                lsv_s32_t chunk_off, lsv_s32_t size, lsv_u8_t type) {
        int ret = 0;
        lsv_volume_io_t vio;

        lsv_volume_io_init(&vio, chunk_id, chunk_off, size, type);

        ret = lsv_volume_chunk_update(volume_proto, &vio, buf);
        if(ret < 0){
                DERROR("chunk[%d] load failed, ret: %d\n", chunk_id, ret);
                ret = -1;
                return ret;
        }

        return ret;
}

//只能查询到log中，新生代chunk的gc_valueup信息，老年代无法查询
//1代表download到ssd中，0表示丢弃
int lsv_rcache_l2u_download_judge(lsv_volume_proto_t *volume_proto, lsv_u32_t chunk_id){
	    lsv_log_info_t *log_info = NULL;
	    lsv_gc_context_t *gc_context = NULL;
	    lsv_gc_logctrl_t* logctrl = NULL;

	    log_info = (lsv_log_info_t*) volume_proto->log_info;
	    gc_context = log_info->gc_context;

	    logctrl = (lsv_gc_logctrl_t*) hash_table_find(gc_context->logctrl_tab, &chunk_id);
	    if (NULL != logctrl) {
	            DINFO_NOP("chunk_id:%u, valueup:%d\n", chunk_id, logctrl->gc_value);
	            if (logctrl->gc_value < 10) {
	            		return 1;
	            }else{
	            		return 0;
	            }
	    }else{
	    		srand((int)gettime());
	    		return rand() % 2;
	    }
	    return 0;
}

int lsv_rcache_chunk_load_judge(lsv_rcache_t * rcache, lsv_u64_t lba, lsv_u32_t chunk_id, lsv_s32_t chunk_off, lsv_s32_t size,
                const raw_io_t *rio){
        int ret = 0;
        lsv_rcache_iotrace_unit *riu = NULL;
        struct list_head * pos, *n;

        if(LSV_RCACHE_CHUNK_NUM_MAX_L2 == 0){
                if(LSV_RCACHE_CHUNK_NUM_MAX == 0){
                        //仅当只有LSV_RCACHE_PAGE_NUM_MAX > 0时，使用page模式
                        return 0;
                }else if(LSV_RCACHE_PAGE_NUM_MAX == 0){
                        //仅当只有LSV_RCACHE_CHUNK_NUM_MAX > 0时，使用chunk模式
                        return 1;
                }
        }

        /*io_list 组织成先进先出队列，尾进，头出，最大大小为LSV_RCACHE_IOTRACE_NUM，循环利用*/
        if(rcache->io_num < LSV_RCACHE_IOTRACE_NUM){
                ret = ymalloc((void **)&riu, sizeof(lsv_rcache_iotrace_unit));
                if(unlikely(ret)){
                        ret = -EINVAL;
                        DERROR("malloc for lsv_rcache_iotrace_unit failed\n");
                        return ret;
                }
                rcache->io_num ++;
                INIT_LIST_HEAD(&riu->list);
                DINFO("LSV_RCACHE chunk_num %d\n", rcache->io_num);
        }else{
                riu = list_entry(rcache->io_list.next, lsv_rcache_iotrace_unit, list);
                list_del_init(&riu->list);
        }
        riu->lba = lba;
        riu->chunk_id = chunk_id;
        riu->chunk_off = chunk_off;
        riu->size = LSV_PAGE_SIZE;
        riu->status = LSV_RCACHE_PAGE;
        list_add_tail(&riu->list, &rcache->io_list);

        if (rcache->io_num <= LSV_RCACHE_IO_NUM_LEAST) {
                return 0;
        }
	
	//if(NULL!=rio && rio->size >= LSV_RCACHE_LOAD_CHUNK_RIO){
	//	return 1;
	//}

        // TODO 此决策过程与具体IO无关，只是最近IO访问到的chunk数有关
        // TODO 螺旋写入会打乱IO序，所以需要在一个更大的范围内，寻找IO访问模式

        lsv_u32_t chunk_ids[LSV_RCACHE_CHUNK_NUMS_PER_CIRCLE];
        lsv_s32_t i = 0;
        lsv_s32_t j = 0;
        lsv_s32_t cnt = 0;
        list_for_each_prev_safe(pos, n, &rcache->io_list){
                lsv_rcache_iotrace_unit *lriu = list_entry(pos, lsv_rcache_iotrace_unit, list);
                cnt ++;
                // 顺序查找
                for(j = 0; j < i; j++){
                        if (lriu->chunk_id == chunk_ids[j]) {
                                break;
                        }
                }
                if (j == i) {
                        // 没找到，则扩展数组
                        chunk_ids[i++] = lriu->chunk_id;
                }
                if (i == LSV_RCACHE_CHUNK_NUMS_PER_CIRCLE){
                        break;
                }
        }
#if 0
	if(NULL != rio && rio->size >= LSV_RCACHE_LOAD_CHUNK_RIO){
		if(i < LSV_RCACHE_CHUNK_NUMS_PER_CIRCLE_WITH_RIO){
			return 1;
		}
	}
#else
	if(NULL != rio && rio->size > LSV_PAGE_SIZE){
		if(rio->size >= LSV_RCACHE_LOAD_CHUNK_RIO && i < LSV_RCACHE_CHUNK_NUMS_PER_CIRCLE_WITH_RIO){
			return 1;
		}
		if(size > LSV_PAGE_SIZE){
			return 2;
		}
	}
#endif

        // 100个IO最多有10个chunk，则使用load chunk
        if(i < LSV_RCACHE_CHUNK_NUMS_PER_CIRCLE && cnt > LSV_RCACHE_IO_NUM_LEAST){
                return 1;
        }
        return 0;

        /*policy 1 如果rio的size大于4KB，则load整个chunk*/
        if(NULL != rio && rio->size > LSV_PAGE_SIZE){
                ret = 1;
                goto EXIT;
        }

        /*policy 2 如果rio的size小于4k，暂时包括512字节，且chunk_off为4096,则load整个chunk，注：每个chunk前4k为元数据信息，真正数据从4096开始的255个page页*/
        if(chunk_off == LSV_PAGE_SIZE){
                ret = 1;
                goto EXIT;
        }

        /*policy 4 如果io_trace列表中，n次访问之内，访问同一个chunk_id的次数超过m，则load整个chunk， n和m的值暂定，m考虑取2，理由，能访问第二次，就会访问第3次*/
        lsv_s32_t num = 0;
        list_for_each_prev_safe(pos, n, &rcache->io_list){
                num ++;
                if(num == 1){
                        continue;
                }else if(num > 20){
                        break;
                }
                lsv_rcache_iotrace_unit *lriu = list_entry(pos, lsv_rcache_iotrace_unit, list);
                if(lriu->status == LSV_RCACHE_CHUNK){
                        continue;
                }
                if(lriu->chunk_id == chunk_id && num < 20){
                        ret = 1;
                        goto EXIT;
                }
        }

EXIT:
        if(ret == 1){
                riu->status = LSV_RCACHE_CHUNK;
        }
        return ret;
}

int lsv_rcache_l2_cache_write_data(lsv_volume_proto_t *volume_proto, void *buf, lsv_u32_t chunk_id,
                lsv_s32_t chunk_off, lsv_s32_t size, lsv_s8_t type){
        int ret = 0;

        ret = lsv_rcache_write_data(volume_proto, (lsv_s8_t *)buf, chunk_id, chunk_off, size, type);
        if(ret < 0){
                DERROR("chunk lsv chunk_id swap to ssd chunk_id %u, write chunk failed, ret: %d\n", chunk_id, ret);
                return ret;
        }

        return ret;
}
